/*
 * Created by Jaren at 2022/1/29 11:29
 */
import 'dart:typed_data';

import 'package:inmotion_protocol/common/exceptions.dart';
import 'package:inmotion_protocol/common/response.dart';
import 'package:inmotion_protocol/lorin/bean/common/date.dart';
import 'package:inmotion_protocol/lorin/file/lorin_file.dart';
import 'package:inmotion_protocol/util/byte_util.dart';
import 'package:inmotion_protocol/util/log.dart';

class LorinFileResponse extends FileResponse {
  LorinFileResponse(int fileHandle, int originError) : super.byOriginError(originError) {
    error = FileResponse.getFileErrorByOriginError(originError);
  }

  static LorinFileResponse fromBytes(Uint8ClampedList bytes) {
    if (bytes.length == 1) return LorinFileResponse(bytes[0], 0);
    return LorinFileResponse(bytes[0], bytes[1]);
  }
}

class OpenFileResponse extends LorinFileResponse {
  int fileSize = 0;
  Uint8ClampedList check = Uint8ClampedList(0);
  Datetime lastUpdate = Datetime.emptyDate;

  OpenFileResponse(int fileHandle, int error) : super(fileHandle, error);

  static OpenFileResponse fromBytes(Uint8ClampedList bytes) {
    if (bytes.length < 2) throw ProtocolParseException('FileResponse needs 2 bytes at least.');
    var rsp = OpenFileResponse(bytes[0], bytes[1]);
    if (!rsp.isSuccess()) return rsp;

    int minLength = 2 + 4 + 4;
    if (bytes.length < minLength) throw ProtocolParseException('OpenFileResponse needs $minLength bytes at least, current is ${bytes.length}.');

    int offset = 1 + 1;
    rsp.fileSize = ByteUtil.bytes2UIntLe(bytes, offset, 4);
    offset += 4;
    rsp.check = bytes.sublist(offset, offset + 4);
    if (bytes.length >= offset + Datetime.length) {
      var lastUpdate = Datetime();
      Datetime.fromBytes(bytes, offset: offset, output: lastUpdate);
      rsp.lastUpdate = lastUpdate;
    }

    return rsp;
  }
}

class WriteFileResponse extends LorinFileResponse {
  int length = 0;

  WriteFileResponse(int fileHandle, int error, this.length) : super(fileHandle, error);

  static WriteFileResponse fromBytes(Uint8ClampedList bytes) {
    if (bytes.length < 3) throw ProtocolParseException('FileResponse needs 3 bytes at least.');
    return WriteFileResponse(bytes[0], bytes[1], bytes[2]);
  }
}

class ReadFileResponse extends LorinFileResponse {
  int offset = 0;
  Uint8ClampedList content;

  ReadFileResponse(int fileHandle, int error, this.offset, this.content) : super(fileHandle, error);

  static ReadFileResponse fromBytes(Uint8ClampedList bytes) {
    if (bytes.length < 6) throw ProtocolParseException('FileResponse needs 2 bytes at least.');
    var rsp = ReadFileResponse(bytes[0], bytes[1], ByteUtil.bytes2UIntLe(bytes, 2, 4), bytes.sublist(6, bytes.length));
    return rsp;
  }
}

class CheckFileResponse extends LorinFileResponse {
  int checkResult = 0;

  CheckFileResponse(int fileHandle, int error, this.checkResult) : super(fileHandle, error);

  bool isCheckSuccess() {
    return checkResult == 1;
  }

  static CheckFileResponse fromBytes(Uint8ClampedList bytes) {
    if (bytes.length < 3) throw ProtocolParseException('FileResponse needs 3 bytes at least.');
    return CheckFileResponse(bytes[0], bytes[1], bytes[2]);
  }
}

class GetFileCheckResponse extends LorinFileResponse {
  int totalLength; // 4 bytes
  int offset; // 4 bytes
  Uint8ClampedList check;

  GetFileCheckResponse(int fileHandle, int error, this.totalLength, this.offset, this.check) : super(fileHandle, error);

  static GetFileCheckResponse fromBytes(Uint8ClampedList bytes) {
    if (bytes.length < 10) throw ProtocolParseException('GetFileCheckResponse needs 10 bytes at least.');
    return GetFileCheckResponse(bytes[0], bytes[1], ByteUtil.bytes2UIntLe(bytes, 2, 4), ByteUtil.bytes2UIntLe(bytes, 6, 4), bytes.sublist(10, 14));
  }
}

class LorinFileListResponse extends FileListResponse {
  LorinFileListResponse.byOriginError(int originError) : super.byOriginError(originError);

  static LorinFileListResponse fromBytes(Uint8ClampedList bytes) {
    int offset = 0;
    var rsp = LorinFileListResponse.byOriginError(bytes[offset]);
    offset++;
    if (!rsp.isSuccess()) {
      LogUtils.w("fromBytes: Get file list error, code is [${rsp.originError}].");
      return rsp;
    }
    rsp.index = ByteUtil.bytes2UIntLe(bytes, offset, 2);
    offset += 2;
    rsp.remain = ByteUtil.bytes2UIntLe(bytes, offset, 2);
    offset += 2;

    if (offset >= bytes.length) {
      return rsp;
    }

    LorinFile file;
    while ((file = LorinFile.dirFromBytes(bytes, offset, rsp.files)) != LorinFile.invalidFile) {
      int childrenBytes = file.getChildrenBytes();
      int bytesLength = file.bytesLength() + childrenBytes;
      offset += bytesLength;
      if (childrenBytes == 0 || childrenBytes == LorinFile.invalidFileBytesLength) {
        // 该目录为空
        rsp.files.add(file);
      }
      // 对于不为空的目录，不需要添加
    }

    return rsp;
  }
}
